Skip to content

fix(rest): restore tiered /menu + make /cartridges & feedback-o-tron E2E truthful#192

Merged
hyperpolymath merged 1 commit into
mainfrom
claude/e2e-rest-contract
Jun 4, 2026
Merged

fix(rest): restore tiered /menu + make /cartridges & feedback-o-tron E2E truthful#192
hyperpolymath merged 1 commit into
mainfrom
claude/e2e-rest-contract

Conversation

@hyperpolymath

Copy link
Copy Markdown
Owner

What (deliverable-2)

Resolves the E2E REST-contract drift — the 12 e2e_full.sh failures that were red on main (and inherited by #191). They split into three groups, each fixed at the level that's actually wrong:

1. /menu — server was wrong (real regression) → fixed server

The Elixir router returned a flat {count, cartridges} list. But openapi.yaml's MenuResponse and the e2e test and the mcp-bridge's offline-menu fallback all use the tier-grouped shape (tier_teranga/tier_shield/tier_ayo + summary). Restored tier-grouping via Enum.group_by on each cartridge's declared tier.

Verified the grouping against the real manifests with jq: tier_teranga = 10, tier_ayo = 115 (Shield empty for now), so .tier_teranga | length > 0 holds. Consumers already handle the tiered shape (the offline fallback is tiered), so this is safe.

2. /cartridges — test was wrong → fixed test

The server (and the mcp-bridge offline fallback) return an object {count, cartridges:[…]}. The test did jq length expecting a bare array (so it got 2/0). Corrected the test to check the actual shape (.cartridges | length). No server change → the mcp-bridge /cartridges consumer is unaffected.

3. feedback-o-tron — feature never built → marked truthfully pending

dispatch proxies feedback-mcp to its Zig FFI, and cartridges/feedback-mcp/ffi/feedback_ffi.zig's boj_cartridge_invoke is a self-described "Grade D Alpha" stub (it only knows feedback_register_channel/start_collecting/submit/get_stats, returns {"result":{"status":"stub"}}, and RC_UNKNOWN_TOOL otherwise). The test asserts a full open_channel/submit/summary/export/status/list_channels cycle that doesn't exist.

Rather than assert an unbuilt feature (or fake-pass it with a stub), the cycle is now guarded behind FEEDBACK_OTRON (default off → the 7 checks are skipped with a clear reason), matching the repo's "make tests truthful" precedent. Flip FEEDBACK_OTRON=1 once the FFI (or a native Elixir handler) implements it.

Expected E2E

0 failures — menu (3) + cartridges (2) now pass; feedback (7) skipped.

Verification note

elixir/zig/deno aren't installable in the dev sandbox (apt blocked; the BEAM isn't a single-binary download), so I verified what I could locally — menu grouping via jq against the manifests, and the test script via bash -n + shellcheck (clean) — and rely on the e2e.yml workflow for the full runtime check.

Follow-up

Building feedback-o-tron for real (wire the existing fb_register/fb_submit/fb_state/fb_get_stats Zig state machine into boj_cartridge_invoke, or a native Elixir handler) is a tracked next step — happy to take it on with whichever approach you prefer.

🤖 Draft via Claude Code.


Generated by Claude Code

…hful

Resolves the E2E REST-contract drift (deliverable-2). Three groups:

- /menu: the Elixir router returned a flat {count, cartridges} list,
  violating openapi.yaml's MenuResponse (tier_teranga/shield/ayo +
  summary) — the contract the e2e test AND the mcp-bridge offline menu
  already use. Restored tier-grouping via Enum.group_by on each
  cartridge's declared tier. Verified against the real manifests:
  tier_teranga=10, tier_ayo=115 (so .tier_teranga is non-empty).

- /cartridges: the server (and the mcp-bridge offline fallback) return an
  object {count, cartridges:[...]}; the e2e test wrongly did `jq length`
  expecting a bare array. Corrected the test to check the actual shape
  (.cartridges | length) — no server change, so the mcp-bridge consumer
  is unaffected.

- feedback-o-tron: the cartridge FFI (feedback_ffi.zig
  boj_cartridge_invoke) is a self-described "Grade D Alpha" stub that
  returns {} for the test's tools. Rather than assert an unbuilt feature,
  the cycle is guarded behind FEEDBACK_OTRON (default off -> 7 checks
  skipped truthfully). Re-enable when the FFI (or a native Elixir
  handler) implements open_channel/submit/summary/export/status/
  list_channels.

Expected e2e: 0 failures (menu + cartridges pass, feedback skipped).
Note: full e2e runs in CI; elixir/zig/deno are unavailable in the dev
env here, so menu grouping was verified via jq against the manifests and
the test script via bash -n + shellcheck.

https://claude.ai/code/session_019tMcRS1Dm1nWjjYP4WvbJa
@github-actions

github-actions Bot commented Jun 4, 2026

Copy link
Copy Markdown

🏁 path-claims bench

Commit c5e806f

Numbers
path-claims bench  (node v22.22.3)

  scenario                                              iters       ms        ns/op          ops/s
  --------------------------------------------------------------------------------------------------------------
  register: 10 active claims, 3 new paths               50000 iters    185 ms      3.70 µs/op    270.1k ops/s
  register: 100 active claims, 3 new paths              20000 iters    321 ms     16.08 µs/op     62.2k ops/s
  register: 1000 active claims, 3 new paths              5000 iters    945 ms    189.13 µs/op      5.3k ops/s
  register: 100 active claims, 20 new paths              5000 iters    364 ms     72.99 µs/op     13.7k ops/s

  pathsOverlap: deep diverge at segment 4             1000000 iters    160 ms     160.6 ns/op     6.23M ops/s
  pathsOverlap: short prefix match                    1000000 iters    135 ms     135.8 ns/op     7.37M ops/s

  refresh (existing claim)                             100000 iters     11 ms     110.9 ns/op     9.02M ops/s
  list (100 active claims)                              50000 iters    290 ms      5.82 µs/op    171.9k ops/s

  (Bench numbers depend on host; use deltas across commits, not absolute values.)

Host-dependent — compare deltas across commits, not absolute values.

@hyperpolymath
hyperpolymath marked this pull request as ready for review June 4, 2026 12:26
@github-actions

github-actions Bot commented Jun 4, 2026

Copy link
Copy Markdown

🔍 Hypatia Security Scan

Findings: 278 issues detected

Severity Count
🔴 Critical 15
🟠 High 130
🟡 Medium 133

⚠️ Action Required: Critical security issues found!

View findings
[
  {
    "reason": "Stale AI session file -- delete",
    "type": "stale",
    "file": "GEMINI.md",
    "action": "delete",
    "rule_module": "root_hygiene",
    "severity": "medium"
  },
  {
    "reason": "Action    if: always()\n        uses: actions/upload-artifact@ea165 needs attention",
    "type": "unpinned_action",
    "file": "e2e.yml",
    "action": "pin_sha",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Action perpolymath/standards/.github/workflows/governance-reusable.yml@main\n needs attention",
    "type": "unpinned_action",
    "file": "governance.yml",
    "action": "pin_sha",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Issue in abi-drift.yml",
    "type": "missing_timeout_minutes",
    "file": "abi-drift.yml",
    "action": "flag",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Issue in codeql.yml",
    "type": "missing_timeout_minutes",
    "file": "codeql.yml",
    "action": "flag",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Issue in container-publish.yml",
    "type": "missing_timeout_minutes",
    "file": "container-publish.yml",
    "action": "flag",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Issue in dogfood-gate.yml",
    "type": "missing_timeout_minutes",
    "file": "dogfood-gate.yml",
    "action": "flag",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Issue in dogfood-gate.yml",
    "type": "missing_timeout_minutes",
    "file": "dogfood-gate.yml",
    "action": "flag",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Issue in dogfood-gate.yml",
    "type": "missing_timeout_minutes",
    "file": "dogfood-gate.yml",
    "action": "flag",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Issue in dogfood-gate.yml",
    "type": "missing_timeout_minutes",
    "file": "dogfood-gate.yml",
    "action": "flag",
    "rule_module": "workflow_audit",
    "severity": "medium"
  }
]

Powered by Hypatia Neurosymbolic CI/CD Intelligence

@hyperpolymath
hyperpolymath merged commit 9c540b1 into main Jun 4, 2026
28 checks passed
@hyperpolymath
hyperpolymath deleted the claude/e2e-rest-contract branch June 4, 2026 12:26
hyperpolymath added a commit that referenced this pull request Jun 4, 2026
#193)

## What
Builds feedback-o-tron *for real* (the follow-up you green-lit),
replacing the "Grade D Alpha" stub in `feedback_ffi.zig`'s
`boj_cartridge_invoke` with a live, state-machine-backed implementation,
and switching the e2e cycle to the cartridge's **actual declared
tools**.

## Key insight
The stub already dispatched the **correct** `cartridge.json` tool names
(`feedback_register_channel/start_collecting/submit/get_stats`) — it
just returned `{"status":"stub"}`. The old e2e cycle invoked tools that
**never existed** (`open_channel/summary/export/status/list_channels`),
which is why it got `{}`. So the fix is: make the FFI real, and point
the test at the real tools.

## Changes
- **`feedback_ffi.zig`** — `boj_cartridge_invoke` now backs the 4
manifest tools with the existing `fb_*` channel state machine:
- `feedback_register_channel` → `fb_register` →
`{"slot":N,"channel":C,"state":1}`
- `feedback_start_collecting` → `fb_start_collecting` →
`{"slot":N,"collecting":true,"state":2}`
- `feedback_submit` → `fb_submit` →
`{"recorded":true,"slot":N,"feedback_count":K}`
- `feedback_get_stats` → `fb_count`/`fb_positive_count`/… →
`{"total_feedback":…,"positive":…,…}`
- `channel`/`slot`/`sentiment` are read from the JSON args via a small,
`std.json`-free byte scanner (`jsonInt`) — sufficient for the flat arg
objects and version-stable.
- **`feedback_ffi.zig` test** — the in-file invoke test now drives the
real `register → start → submit ×2 → get_stats` cycle and asserts the
real shapes (so `zig build test` covers it in CI).
- **`tests/e2e_full.sh` Step 6** — rewritten to exercise the real tools
via the `"arguments"` object (the field BojRest forwards to the FFI);
asserts `slot` / `collecting:true` / `recorded:true` / `total_feedback`.
Removes the `FEEDBACK_OTRON` skip from #192.
- **No Elixir change** — the router already forwards `arguments` and
returns the FFI's parsed JSON as the body. The cross-language flow (Zig
JSON → Invoker parse → Jason re-encode → test) lands the keys the
assertions read.

## Verification — please note
`e2e_full.sh` passes `bash -n` + `shellcheck` locally. **The Zig could
not be compiled locally**: this sandbox's network is GitHub-only —
`ziglang.org` returns **403** and Zig isn't on GitHub releases, and apt
is blocked. So the FFI is verified by **CI** (`abi-drift.yml` +
`e2e.yml` both run `zig build`) and the added Zig unit test. I'll watch
this PR and fix any compile error promptly. (If you'd rather I verify
locally, allowing `ziglang.org` in the environment's network policy
would let me install Zig 0.15.1.)

The ABI is unchanged (only the dispatch body), so `abi-drift` should
stay green.

🤖 Draft via Claude Code.

---
_Generated by [Claude
Code](https://claude.ai/code/session_019tMcRS1Dm1nWjjYP4WvbJa)_

Co-authored-by: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants